跳到主要内容

Gin validator 数据校验库

官方文档也有这些内容,这里只是把常用的操作记录下来,方便 Copy

这里使用的是 go-playground/validator 数据校验库

# 安装 go-playground/validator
go get github.com/go-playground/validator/v10

项目文档 Package validator Documentation

在数据结构上加上校验项

// User contains user information
type User struct {
FirstName string `validate:"required"`
LastName string `validate:"required"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}

// Address houses a users address information
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Planet string `validate:"required"`
Phone string `validate:"required"`
}

使用起来很简单

var validate *validator.Validate

validate = validator.New()

校验数据结构

func validateStruct() {

address := &Address{
Street: "Eavesdown Docks",
Planet: "Persphone",
Phone: "none",
}

user := &User{
FirstName: "Badger",
LastName: "Smith",
Age: 135,
Email: "Badger.Smith@gmail.com",
FavouriteColor: "#000-",
Addresses: []*Address{address},
}

// returns nil or ValidationErrors ( []FieldError )
err := validate.Struct(user)
if err != nil {

// this check is only needed when your code could produce
// an invalid value for validation such as interface with nil
// value most including myself do not usually have code like this.
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}

// 输出校验错误 .(validator.ValidationErrors)是断言
for _, err := range err.(validator.ValidationErrors) {

fmt.Println(err.Namespace())
fmt.Println(err.Field())
fmt.Println(err.StructNamespace())
fmt.Println(err.StructField())
fmt.Println(err.Tag())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())
fmt.Println(err.Type())
fmt.Println(err.Value())
fmt.Println(err.Param())
fmt.Println()
}

// from here you can create your own error messages in whatever language you wish
return
}

// save user to database
}

validator 返回的错误实际上只有两种,一种是参数错误,一种是校验错误。

  • 输入参数错误时,返回 InvalidValidationError 类型;
  • 字段违反约束时返回 ValidationErrors

它们都实现了 error 接口。而且 ValidationErrors 是一个错误切片,它保存了每个字段违反的每个约束信息:

单独校验

除了上面那种对数据结构校验,也可以单独校验某个数据项

func validateVariable() {

myEmail := "example.gmail.com"

errs := validate.Var(myEmail, "required,email")

if errs != nil {
fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
return
}

// email ok, move on
}

校验多个变量

在一些很简单的情况下,我们仅仅想对两个变量进行比较,如果每次都要先定义结构和tag就太繁琐了。validator 提供了 VarWithValue() 方法,我们只需要传入要验证的两个变量和约束即可

func main() {
name1 := "dj"
name2 := "dj2"

validate := validator.New()
fmt.Println(validate.VarWithValue(name1, name2, "eqfield"))

fmt.Println(validate.VarWithValue(name1, name2, "nefield"))
}

数据自动绑定加校验

创建工具函数

package app

import (
"github.com/astaxie/beego/validation"
"github.com/gin-gonic/gin"
"net/http"

"github.com/EDDYCJY/go-gin-example/pkg/e"
)

// BindAndValid binds and validates data
func BindAndValid(c *gin.Context, form interface{}) (int, int) {
err := c.Bind(form)
if err != nil {
return http.StatusBadRequest, e.INVALID_PARAMS
}

valid := validation.Validation{}
check, err := valid.Valid(form)
if err != nil {
return http.StatusInternalServerError, e.ERROR
}
if !check {
MarkErrors(valid.Errors)
return http.StatusBadRequest, e.INVALID_PARAMS
}

return http.StatusOK, e.SUCCESS
}

使用这个工具函数:

// @Summary Update article tag
// @Produce json
// @Param id path int true "ID"
// @Param name body string true "Name"
// @Param state body int false "State"
// @Param modified_by body string true "ModifiedBy"
// @Success 200 {object} app.Response
// @Failure 500 {object} app.Response
// @Router /api/v1/tags/{id} [put]
func EditTag(c *gin.Context) {
var (
appG = app.Gin{C: c}
form = EditTagForm{ID: com.StrTo(c.Param("id")).MustInt()}
)

httpCode, errCode := app.BindAndValid(c, &form)
if errCode != e.SUCCESS {
appG.Response(httpCode, errCode, nil)
return
}

tagService := tag_service.Tag{
ID: form.ID,
Name: form.Name,
ModifiedBy: form.ModifiedBy,
State: form.State,
}

exists, err := tagService.ExistByID()
if err != nil {
appG.Response(http.StatusInternalServerError, e.ERROR_EXIST_TAG_FAIL, nil)
return
}

if !exists {
appG.Response(http.StatusOK, e.ERROR_NOT_EXIST_TAG, nil)
return
}

err = tagService.Edit()
if err != nil {
appG.Response(http.StatusInternalServerError, e.ERROR_EDIT_TAG_FAIL, nil)
return
}

appG.Response(http.StatusOK, e.SUCCESS, nil)
}

这个结构体:

type EditTagForm struct {
ID int `form:"id" valid:"Required;Min(1)"`
Name string `form:"name" valid:"Required;MaxSize(100)"`
ModifiedBy string `form:"modified_by" valid:"Required;MaxSize(100)"`
State int `form:"state" valid:"Range(0,1)"`
}

Reference